package qj.ac.unifs;

import static qj.util.FileUtil.*;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.zip.ZipOutputStream;

import qj.util.FileUtil;
import qj.util.IOUtil;
import qj.util.ZipUtil;
import qj.util.funct.F2;
import qj.util.funct.Fs;
import qj.util.funct.P2;

public class UnifyWriter {
	
	HashMap<String, byte[]> store = new HashMap<String, byte[]>();
	
	List<FileSet> fileSets = new LinkedList<FileSet>();
	

	public static void main(String[] args) {
		File branchFolder = new File("build/branch");
		
		UnifyWriter unifyWriter = new UnifyWriter();
		
		for (File dir : branchFolder.listFiles()) {
			if (dir.isFile()) {
				continue;
			}
			
			unifyWriter.writeDir(new File(dir, "client/data"), "branch:" + dir.getName());
		}
		
		OutputStream out = FileUtil.fileOutputStream(new File("unify.zip"));
		unifyWriter.writeZip(out);
		IOUtil.close(out);
	}
	
	public UnifyReader getReader() {
		return new UnifyReader(fileSets, store);
	}

	public void writeDir(File dir, String setName) {
		writeDir(dir, null, setName);
	}
	
	public void writeDir(File dir, final F2<byte[],String,byte[]> contentFilter, String setName) {
		final LinkedList<FileMd5> files = new LinkedList<FileMd5>();
		
		addSet(setName, files);
		
		FileUtil.eachFile(dir, new P2<File,String>() {public void e(File file, String relPath) {
			String relFilePath = (relPath + "/" + file.getName()).replaceAll("\\\\", "/");
			
			byte[] content = FileUtil.readFileToBytes(file);
			if (contentFilter != null) {
				content = contentFilter.e(content, relFilePath);
			}
			String md5 = IOUtil.md5(new ByteArrayInputStream(content));
			
			addStore(md5,content);
			
			files.add(new FileMd5(relFilePath, md5));
		}}, Fs.not(filter));
	}

	protected void addStore(String md5, byte[] content) {
		if (store.containsKey(md5)) {
			return;
		}
		store.put(md5, content);
	}

	private void addSet(String setName, final LinkedList<FileMd5> files) {
		FileSet fileSet = new FileSet();
		fileSet.name = setName;
		fileSet.files = files;
		fileSets.add(fileSet);
	}
	

	public void writeZip(OutputStream outS) {

		ZipOutputStream out = new ZipOutputStream(outS);
		P2<byte[], String> zipWrite = ZipUtil.zipWriteBytes(out, null);
		
		for (Entry<String, byte[]> unifyEntry : store.entrySet()) {
			zipWrite.e(unifyEntry.getValue(), "store/" + unifyEntry.getKey());
		}
		
		zipWrite.e(fileSetString(fileSets).getBytes(), "fileSets.txt");
		
		IOUtil.close(out);
	}

	private static String fileSetString(List<FileSet> fileSets) {
		StringBuilder sb = new StringBuilder();
		for (FileSet fileSet : fileSets) {
			sb.append(fileSet.name).append("\r\n");
			for (FileMd5 file : fileSet.files) {
				sb.append(file.md5 + ":" + file.filePath + "\r\n");
			}
			sb.append("\r\n\r\n");
		}
		return sb.toString();
	}
}